package mt.dummy;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

import mt.Order;
import mt.comm.ClientSideMessage;
import mt.comm.ServerComm;
import mt.comm.ServerSideMessage;

/**
 * ServerCommDummy is used by the server to communicate with the clients.
 * 
 * @author Pedro,Rita, Miguel
 *
 */
public class ServerCommDummy implements ServerComm {
        /**
         * The Port of the ServerCommDummy
         */
        public static final int PORTO = 1901;

        /**
         * The ServerSocket of the ServerCommDummy
         */
        private ServerSocket serverSocket;

        /**
         * The queue that has ServerMessage
         */
        public BlockingQueue<ServerMessage> q = new ArrayBlockingQueue<ServerMessage>(
                        100);

        /**
         * The hashmap that has the nickname of the client and the output to answer
         * him.
         */

        public HashMaps<String, ObjectOutputStream> maps = new HashMaps<String, ObjectOutputStream>();

        @Override
        public void start() {
                new StartServicing().start();
        }

        /**
         * StartServicing setups a server socket and stars another thread that
         * listens for connections.
         */
        class StartServicing extends Thread {

                @Override
                public void run() {
                        Socket socket = null;
                        try {
                                serverSocket = new ServerSocket(PORTO);
                                System.out.println("Server Waiting for Clients");
                                while (true) {
                                        // Waits for clients t connect
                                        socket = serverSocket.accept();
                                        ObjectOutputStream out = new ObjectOutputStream(
                                                        socket.getOutputStream());
                                        ObjectInputStream in = new ObjectInputStream(
                                                        socket.getInputStream());
                                        new DealWithClient(in, out).start();
                                        System.out.println("A new Client has been accepted");
                                }
                        } catch (IOException e) {
                                e.printStackTrace();
                        } finally {
                                try {
                                        socket.close();
                                } catch (IOException e) {
                                        e.printStackTrace();
                                }
                        }
                }
        }

        /**
         * This thread listens for connections. This Threads receives and processed
         * the ServerMessage.
         */

        class DealWithClient extends Thread {

                private ObjectInputStream in;
                private ObjectOutputStream out;
                private String clientName;
                private boolean running;

                public DealWithClient(ObjectInputStream in, ObjectOutputStream out) {
                        this.in = in;
                        this.out = out;
                }

                public ObjectOutputStream getOut() {
                        return out;
                }

                @Override
                public void run() {
                        running = true;
                        try {
                                while (running) {
                                        ServerMessage message = (ServerMessage) in.readObject();
                                        if (!running) {
                                                break;
                                        }
                                        switch (message.getType()) {
                                        case CONNECTED:
                                                System.out.println("Quero conectar => "
                                                                + message.getSenderNickname());
                                                if (!maps.contains(message.getSenderNickname())) {
                                                        clientName = message.getSenderNickname();
                                                        maps.addEntry(message.getSenderNickname(), out);
                                                        q.add(message);
                                                } else {
                                                        ClientMessage msg = new ClientMessage(
                                                                        ClientSideMessage.Type.ERROR,
                                                                        "Name already exists!");
                                                        out.writeObject(msg);
                                                }
                                                break;
                                        case NEW_ORDER:
                                                q.add(message);
                                                break;
                                        case DISCONNECTED:
                                                ServerMessage msg = new ServerMessage(
                                                                ServerSideMessage.Type.DISCONNECTED,
                                                                clientName, null);
                                                q.add(msg);
                                                break;
                                        default:
                                                break;
                                        }
                                }
                        } catch (IOException e) {
                                ServerMessage msg = new ServerMessage(
                                                ServerSideMessage.Type.DISCONNECTED, clientName, null);
                                q.add(msg);
                                q.clear();
                        } catch (ClassNotFoundException e) {
                                e.printStackTrace();
                        } finally {
                                running = false;
                        }
                }
        }

        @Override
        public boolean hasNextMessage() {
                return !q.isEmpty();
        }

        @Override
        public ServerSideMessage getNextMessage() {
                return q.poll();
        }

        @Override
        public void sendError(String toNickname, String error) {
                ObjectOutputStream out = maps.get(toNickname);
                try {
                        out.writeObject(new ClientMessage(ClientSideMessage.Type.ERROR,
                                        error + " => " + toNickname));
                        out.close();
                } catch (IOException e) {
                        e.printStackTrace();
                }
        }

        @Override
        public boolean clientIsConnected(String nickname) {
                return maps.contains(nickname);
        }

        @Override
        public void disconnectClient(String nickname) {
                maps.remove(nickname);
        }

        @Override
        public void sendOrder(String receiversNickname, Order order) {
                if (clientIsConnected(receiversNickname)) {
                        ObjectOutputStream out = maps.get(receiversNickname);
                        try {
                                System.out.println("ServerComm Envia: " + order.toString());
                                out.writeObject(new ClientMessage(ClientSideMessage.Type.ORDER,
                                                order));
                        } catch (IOException e) {
                                System.out.println("Sai do Server");
                        }
                }

        }

}